// system/system.go
package system

import (
	"crypto/rand"
	"crypto/sha256"
	"log"
	"math/big"
)

// SystemParams 包含了协议的所有公共参数
type SystemParams struct {
	P         *big.Int // 大素数 p (Z_p* 乘法群)
	P_minus_1 *big.Int // p-1 (指数运算的模)
	Q         *big.Int // 素数 q (基础运算的模)
	QPrime    *big.Int // 素数 q' (a 的来源域)
	G         *big.Int // 生成元 g
	A_val     *big.Int // 公开值 a，一个大整数
	Gb        *big.Int // 公钥部分 g^b mod p
	A_IDs     []string // 所有CSP的ID列表
}

// AuditRequest 封装了数据所有者发起的审计请求所需的所有信息
type AuditRequest struct {
	FileID  string
	Indices []int
	Weights []*big.Int
	Lambda1 *big.Int
	Lambda2 *big.Int
}

// H1 是协议中的第一个哈希函数: {0,1}* -> Z_q
func H1(input string, q *big.Int) *big.Int {
	hashBytes := sha256.Sum256([]byte(input))
	hashInt := new(big.Int).SetBytes(hashBytes[:])
	return hashInt.Mod(hashInt, q)
}

// H2 是协议中的第二个哈希函数: {0,1}* -> Z_q
func H2(input string, q *big.Int) *big.Int {
	prefixedInput := "H2_prefix_" + input
	hashBytes := sha256.Sum256([]byte(prefixedInput))
	hashInt := new(big.Int).SetBytes(hashBytes[:])
	return hashInt.Mod(hashInt, q)
}

// NewSystemParams 初始化系统参数。
// 这里的参数选择至关重要，必须满足 q << q' < p，以确保 Y < a 的条件有很大概率被满足。
func NewSystemParams(cspIDs []string) (*SystemParams, error) {
	log.Println("[SYS] Generating system parameters...")
	p, err := rand.Prime(rand.Reader, 512)
	if err != nil {
		return nil, err
	}

	q, err := rand.Prime(rand.Reader, 160)
	if err != nil {
		return nil, err
	}

	// qPrime 的比特长度必须足够大，以保证 a 的取值空间远大于 Y 的可能值。
	var qPrime *big.Int
	for {
		qPrime, err = rand.Prime(rand.Reader, 511)
		if err != nil {
			return nil, err
		}
		if qPrime.Cmp(p) < 0 {
			break
		}
	}

	aVal, err := rand.Int(rand.Reader, qPrime)
	if err != nil {
		return nil, err
	}
	if aVal.Cmp(big.NewInt(0)) == 0 {
		aVal = big.NewInt(1)
	}

	p_minus_1 := new(big.Int).Sub(p, big.NewInt(1))

	var g *big.Int
	for {
		g, err = rand.Int(rand.Reader, p)
		if err != nil {
			return nil, err
		}
		if g.Cmp(big.NewInt(0)) != 0 {
			break
		}
	}

	log.Printf("[SYS] P (bit %d), Q' (bit %d), Q (bit %d)", p.BitLen(), qPrime.BitLen(), q.BitLen())

	return &SystemParams{
		P:         p,
		P_minus_1: p_minus_1,
		Q:         q,
		QPrime:    qPrime,
		G:         g,
		A_val:     aVal,
		A_IDs:     cspIDs,
		Gb:        new(big.Int),
	}, nil
}
